﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Excel = Microsoft.Office.Interop.Excel;

namespace TextExtract
{
    class  CharsetConv {
        private static string root_path;
        private static Encoding encode;
        private static List<TextInfo> info_list = new List<TextInfo>();
        private static IEnumerable<string> file_filters;

        public static bool ExtractFromDir (string path, string file, IEnumerable<string> filters, Encoding en) {
            bool err = true;

            if (Directory.Exists(path)) {
                root_path = path + "\\";
                encode = en;
                info_list.Clear();
                file_filters = filters;
                try {
                    GetTextInfo(path);
                    File.Delete(file);
                    WriteToFile(file);
                    err = false;
                } catch {}
            }
            return err;
        }
        private static void WriteToFile (string path) {
            Excel.Application app = new Excel.Application();
            Excel.Workbook workbook = app.Application.Workbooks.Add(true);
            Excel.Worksheet worksheet = (Excel.Worksheet)workbook.Worksheets.get_Item(1);

            string[] title = TextInfo.GetTitle().Split(',');
            for (int i = 1; i <= title.Length; i++) app.Cells[1, i] = title[i - 1];

            int idx, extra = 2;
            int count = info_list.Count;
            for (int i = 0; i < count; i++) {
                idx = i + extra;
                app.Cells[idx, 1] = info_list[i].file;
                app.Cells[idx, 2] = info_list[i].line;
                app.Cells[idx, 3] = info_list[i].offset;
                app.Cells[idx, 4] = info_list[i].length;
                app.Cells[idx, 5] = info_list[i].text;
                app.Cells[idx, 6] = info_list[i].trans;                  // 赋值到excel里的字符不能有‘=’
            }
            worksheet.SaveAs(path);
            app.Quit();  
        }
        private static void GetTextInfo (string path) {
            if (Directory.Exists(path)) {
                foreach (string dir in Directory.GetDirectories(path)) {
                    GetTextInfo(dir);
                }
                foreach (string filter in file_filters) {
                    foreach (string file in Directory.GetFiles(path, filter)) GetTextInfo(file);
                }
            } else if (File.Exists(path)) {
                ExtractText(path);
            }
        }
        private static void ExtractText (string file_path) {
            int limit = 0x80;
            string content;
//             if (src_encode.HeaderName.Equals("gb2312")) limit = 0xA0;
//             else if (src_encode.HeaderName.Equals("iso-2022-jp")) limit = 0x80;
//             else if (src_encode.HeaderName.Equals("big5")) limit = 0x80;

            string[] line = File.ReadAllLines(file_path, encode);
            int comment, begin, end, count = line.Length;
            for (int i = 0; i < count; i++) {
                string str = line[i];
                comment = str.IndexOf("//");
                begin = str.IndexOf('"');
                if (begin >= 0 && (comment < 0 || begin < comment)) {
                    if (comment > 0) end = str.LastIndexOf('"', comment);
                    else end = str.LastIndexOf('"');
                    if (end < 0 || end <= begin) continue;

                    TextInfo item = new TextInfo();
                    item.length = (end - begin) - 1;
                    content = str.Substring(begin + 1, item.length);
                    bool jump = true;
                    for (int j = 0; j < content.Length; j++) {
                        if (content[j] >= limit) {
                            jump = false;
                            break;
                        }
                    }
                    if (jump) continue;
                    item.file = file_path.Replace(root_path, "");
                    item.line = i + 1;
                    item.offset = begin + 1;
                    item.text = content;
                    info_list.Add(item);
                }
            }
        }


        public static bool PushbackToDir (string path, string file, Encoding en, bool addAt = false) {
            bool err = true;
            if (Directory.Exists(path) && File.Exists(file)) {
                root_path = path + "\\";
                encode = en;
                Dictionary<string, List<TextInfo>> dict = LoadTextInfo(file);
                foreach (List<TextInfo> list in dict.Values) {
                    WritebackToFile(list, addAt);
                }
                err = false;
            }
            return err;
        }
        private static Dictionary<string, List<TextInfo>> LoadTextInfo (string file) {
            TextInfo info;
            List<TextInfo> list;
            Dictionary<string, List<TextInfo>> dict = new Dictionary<string, List<TextInfo>>();

            Excel.Application app = new Excel.Application();
            Excel.Workbook workbook = app.Workbooks.Open(file);
            Excel.Worksheet worksheet = (Excel.Worksheet)workbook.Worksheets.get_Item(1);
            int row = worksheet.UsedRange.Rows.Count;

            Excel.Range range = (Excel.Range)worksheet.get_Range("A1", "F" + row);
            object[,] val = (object[,])range.Value2;

            for (int i = 2; i <= row; i++) {
                info = new TextInfo();
                info.file = val[i, 1].ToString();
                if (!dict.TryGetValue(info.file, out list)) {
                    list = new List<TextInfo>();
                    dict.Add(info.file, list);
                }
                info.line = TryGetInt(val[i, 2]);
                info.offset = TryGetInt(val[i, 3]);
                info.length = TryGetInt(val[i, 4]);
                info.text = TryGetString(val[i, 5]);
                info.trans = TryGetString(val[i, 6]);
                list.Add(info);
            }
            workbook.Close(false);
            app.Quit();
            return dict;
        }
        static int TryGetInt (object obj, int def = 0) {
            if (obj == null) return def;
            return int.Parse(obj.ToString());
        }
        static string TryGetString (object obj, string def = null) {
            if (obj == null) return def;
            return obj.ToString();
        }

        private static void WritebackToFile (List<TextInfo> list, bool addAt) {
            string file_path = root_path + list[0].file;
            string[] lines = File.ReadAllLines(file_path, encode);
            bool dirty = false;

            foreach (TextInfo info in list) {
                if (!string.IsNullOrEmpty(info.trans)) {
                    int idx = info.line - 1;
                    string str = lines[idx];
                    str = str.Remove(info.offset, info.length);
                    str = str.Insert(info.offset, info.trans);
                    if (addAt) str = str.Insert(info.offset-1, "@");
                    lines[idx] = str;
                    if (!dirty) dirty = true;
                }
            }
            if (dirty) File.WriteAllLines(file_path, lines, encode);
        }
        private static string ConvertString (string str, Encoding from, Encoding to) {
            byte[] origin = from.GetBytes(str);
            byte[] conv = Encoding.Convert(from, to, origin);
            return to.GetString(conv);
        }

        private static string GetString (string str, Encoding from, Encoding to) {
            byte[] origin = from.GetBytes(str);
            return to.GetString(origin);
        }

        class TextInfo {
            public string file;         // file name
            public int line;            // line number in current file
            public int offset;          // offset in current line of origin text
            public int length;          // length of origin text
            public string text;         // the text will be translate
            public string trans;        // the translated text who need to writeback
            public static string GetTitle () { return "file,line,offset,length,text,trans"; }
            public override string ToString () {
                return string.Format("{0},{1},{2},{3},{4},{5}", file, line, offset, length, text, trans);
            }
        }
    }
}
